.cpu cortex-m4
.syntax unified
.thumb
.text

;/*
;-------------------------------------------------------------------------
;有关内核外设寄存器定义可参考官方文档：STM32F10xxx Cortex-M3 programming manual
;系统控制块外设SCB地址范围：0xE000ED00-0xE000ED3F
;-------------------------------------------------------------------------
; */
.equ    SCB_VTOR,           0xE000ED08              /* Vector Table Offset Register  向量表偏移寄存器*/
.equ    NVIC_INT_CTRL,      0xE000ED04              /* interrupt control state register  中断控制状态寄存器*/
.equ    NVIC_SYSPRI2,       0xE000ED20              /* system priority register (2)  系统优先级寄存器(2) */
.equ    NVIC_PENDSV_PRI,    0xFFFF0000              /* PendSV and SysTick priority value (lowest)  PendSV 优先级值 (lowest)*/
.equ    NVIC_PENDSVSET,     0x10000000              /* value to trigger PendSV exception  触发PendSV exception的值*/







;/*
; ************************************************************************
;                                 全局变量      
; 使用IMPORT关键字导入一些全局变量，这三个全局变量在cpuport.c中定义                        
; *************************************************************************
;*/
    .extern rt_thread_switch_interrupt_flag  ;/* PendSV中断服务函数执行标志 */
    .extern rt_interrupt_from_thread         ;/* 存储上一个线程的栈的sp的指针 */
    .extern rt_interrupt_to_thread           ;/* 存储下一个将要运行的线程的栈的sp的指针 */



;/*
; *-----------------------------------------------------------------------
; * 函数原型：void rt_hw_context_switch_to(rt_uint32 to);
; * r0 --> to
; * 该函数用于开启第一次线程切换
; *-----------------------------------------------------------------------
; */
.global rt_hw_context_switch_to
.type rt_hw_context_switch_to, %function
rt_hw_context_switch_to:
    /* 设置rt_interrupt_to_thread的值 */
    LDR r1, =rt_interrupt_to_thread    /* 将rt_interrupt_to_thread的地址加载到r1 */
    STR r0, [r1]                       /* 将r0的值存储到rt_interrupt_to_thread */  // r0的值为入参，即即将运行的线程的栈指针sp

#if defined (__VFP_FP__) && !defined(__SOFTFP__)
    /* CLEAR CONTROL.FPCA */
    MRS     r2, CONTROL         /* read */
    BIC     r2, #0x04           /* modify */
    MSR     CONTROL, r2         /* write-back */
#endif

    /* set from thread to 0 */
    /* 设置rt_interrupt_from_thread的值为0，表示启动第一次线程切换  */
    LDR r1, =rt_interrupt_from_thread     /* 将rt_interrupt_from_thread的地址加载到r1*/
    MOV r0, #0x0                           /* 配置r0等于0*/
    STR r0, [r1]                           /* 将r0的值存储到rt_interrupt_from_thread */

    /* set interrupt flag to 1 */
    /* 设置中断标志位rt_thread_switch_interrupt_flag的值为1 */
    LDR     r1, =rt_thread_switch_interrupt_flag   /* 将rt_thread_switch_interrupt_flag的地址加载到r1*/
    MOV     r0, #1                                 /* 配置r0等于1*/
    STR     r0, [r1]                                /* 将r0的值存储到rt_thread_switch_interrupt_flag */

    /* set the PendSV and SysTick exception priority 设置 PendSV 异常的优先级 */
    LDR r0, =NVIC_SYSPRI2
    LDR r1, =NVIC_PENDSV_PRI
    LDR.W   r2, [r0,#0x00]       /* read       */
    ORR     r1,r1,r2             /* modify     */
    STR     r1, [r0]             /* write-back */

    LDR r0, =NVIC_INT_CTRL      /* trigger the PendSV exception (causes context switch)   触发 PendSV 异常 (产生上下文切换)*/
    LDR r1, =NVIC_PENDSVSET
    STR r1, [r0]

    /* restore MSP */
    LDR     r0, =SCB_VTOR
    LDR     r0, [r0]
    LDR     r0, [r0]
    NOP
    MSR     msp, r0

    /* enable interrupts at processor level  开中断 */
    CPSIE   F
    CPSIE   I

    /* clear the BASEPRI register to disable masking priority */
    MOV     r0, #0x00
    MSR     BASEPRI, r0

    /* ensure PendSV exception taken place before subsequent operation */
    DSB
    ISB

    /* never reach here! */


/*
 *-----------------------------------------------------------------------
 * void PendSV_Handler(void);
 * r0 --> switch from thread stack
 * r1 --> switch to thread stack
 * psr, pc, lr, r12, r3, r2, r1, r0 are pushed into [from] stack
 *-----------------------------------------------------------------------
 */

.global PendSV_Handler
.type PendSV_Handler, %function
PendSV_Handler:
    /* 失能中断，为了保护上下文切换不被中断 */
    MRS r2, PRIMASK
    CPSID   I

    /* 获取中断标志位，看看是否为0 */
    LDR r0, =rt_thread_switch_interrupt_flag        /* 获取rt_thread_switch_int_flag的地址到r0*/
    LDR r1, [r0]                                    /* 将flag的值写在r1 */
    CBZ r1, pendsv_exit                             /* 判断r1是否为0，0则跳转至pendsv_exit*/

    /* clear rt_thread_switch_interrupt_flag to 0 */
    MOV r1, #0x00                                   /* 如果r1≠0，则清0*/
    STR r1, [r0]                                    /* 将flag 清0*/

    LDR r0, =rt_interrupt_from_thread
    LDR r1, [r0]
    CBZ r1, switch_to_thread    /* skip register save at the first time *//* 判断r1是否为0，0则跳转至 switch_to_thread */
                                /* 第一次线程切换时 rt_interrupt_from_thread 肯定为0，则跳转到switch_to_thread */
    
    /*******************************************************************************************
    * 当进入PendSVC Handler时，上一个线程运行的环境即：
    * xPSR，PC（线程入口地址），R14，R12，R3，R2，R1，R0（线程的形参）
    * 这些CPU寄存器的值会自动保存到线程的栈中，剩下的r4~r11需要手动保存
    * 1. 获取线程栈指针到r1                                             [MRS r1, psp]
    * 2. 将CPU寄存器r4~r11的值存储到r1指向的地址(每操作一次地址将递减一次) [STMFD   r1!, {r4 - r11}]
    * 3. 加载r0指向值到r0，即r0=rt_interrupt_from_thread                [ LDR r0, [r0] ]
    * 4. 将r1的值存储到r0，即更新线程栈sp                                [ STR     r1, [r0] ]
    ********************************************************************************************/
    
    MRS r1, psp                 /* get from thread stack pointer */

#if defined (__VFP_FP__) && !defined(__SOFTFP__)
    TST     lr, #0x10           /* if(!EXC_RETURN[4]) */
    IT      EQ
    VSTMDBEQ r1!, {d8 - d15}    /* push FPU register s16~s31 */
#endif

    STMFD   r1!, {r4 - r11}     /* push r4 - r11 register */

#if defined (__VFP_FP__) && !defined(__SOFTFP__)
    MOV     r4, #0x00           /* flag = 0 */

    TST     lr, #0x10           /* if(!EXC_RETURN[4]) */
    IT      EQ
    MOVEQ   r4, #0x01           /* flag = 1 */

    STMFD   r1!, {r4}           /* push flag */
#endif

    LDR r0, [r0]
    STR r1, [r0]                /* update from thread stack pointer */

switch_to_thread:
    LDR r1, =rt_interrupt_to_thread       /* rt_interrupt_to_thread是一个全局变量，里面存的是线程栈指针SP的指针 */
    LDR r1, [r1]                           /* 加载rt_interrupt_to_thread的值到r1，即sp指针的指针 */
    LDR r1, [r1]                            /*  加载rt_interrupt_to_thread的值到r1，即sp*/

#if defined (__VFP_FP__) && !defined(__SOFTFP__)
    LDMFD   r1!, {r3}           /* pop flag */
#endif

    LDMFD   r1!, {r4 - r11}     /* pop r4 - r11 register */   /* 将线程栈指针r1(操作之前先递减)指向的内容加载到CPU寄存器r4~r11 */

#if defined (__VFP_FP__) && !defined(__SOFTFP__)
    CMP     r3,  #0             /* if(flag_r3 != 0) */
    IT      NE
    VLDMIANE  r1!, {d8 - d15}   /* pop FPU register s16~s31 */
#endif

    MSR psp, r1                 /* update stack pointer */ /* ;将线程栈指针更新到PSP */

#if defined (__VFP_FP__) && !defined(__SOFTFP__)
    ORR     lr, lr, #0x10       /* lr |=  (1 << 4), clean FPCA. */
    CMP     r3,  #0             /* if(flag_r3 != 0) */
    IT      NE
    BICNE   lr, lr, #0x10       /* lr &= ~(1 << 4), set FPCA. */
#endif

pendsv_exit:
    /* restore interrupt */
    MSR PRIMASK, r2   /* 恢复中断 */

    ORR lr, lr, #0x04 /* 确保异常返回使用的栈指针是PSP，即LR寄存器的位2要为1 */
    BX  lr            /*    异常返回，这个时候栈中的剩下内容将会自动加载到CPU寄存器：
                            xPSR，PC（线程入口地址），R14，R12，R3，R2，R1，R0（线程的形参）
                            同时PSP的值也将更新，即指向线程栈的栈顶*/


/*
 *----------------------------------------------------------------------
 * void rt_hw_context_switch(rt_uint32 from, rt_uint32 to);
 * r0 --> from
 * r1 --> to
 *----------------------------------------------------------------------
 */
.global rt_hw_context_switch_interrupt
.type rt_hw_context_switch_interrupt, %function
.global rt_hw_context_switch
.type rt_hw_context_switch, %function

rt_hw_context_switch_interrupt:
rt_hw_context_switch:
    /* 设置中断标志位rt_thread_switch_interrupt_flag为1 */
    LDR     r2, =rt_thread_switch_interrupt_flag
    LDR     r3, [r2]
    CMP     r3, #1
    BEQ     _reswitch
    MOV     r3, #1
    STR     r3, [r2]

    LDR     r2, =rt_interrupt_from_thread   /* set rt_interrupt_from_thread */
    STR     r0, [r2]       /* 存储r0的值到 rt_interrupt_from_thread ，即上一个线程栈指针sp的指针 */

_reswitch:
    LDR     r2, =rt_interrupt_to_thread     /* set rt_interrupt_to_thread */
    STR     r1, [r2]

    LDR r0, =NVIC_INT_CTRL              /* trigger the PendSV exception (causes context switch) */
    LDR r1, =NVIC_PENDSVSET
    STR r1, [r0]
    BX  LR



/*
 * rt_base_t rt_hw_interrupt_disable();
 */
.global rt_hw_interrupt_disable
.weak rt_hw_interrupt_disable
.type rt_hw_interrupt_disable, %function
rt_hw_interrupt_disable:
    MRS     r0, PRIMASK
    CPSID   I
    BX      LR

/*
 * void rt_hw_interrupt_enable(rt_base_t level);
 */
.global rt_hw_interrupt_enable
.weak rt_hw_interrupt_enable
.type rt_hw_interrupt_enable, %function
rt_hw_interrupt_enable:
    MSR     PRIMASK, r0
    BX      LR